﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Niaofei.Application.Contracts;
using Niaofei.Application.Services;
using Niaofei.Domain.Shared;
using Niaofei.Injcetion;
using SkiaSharp;

namespace Niaofei.Application
{
    [Injection(typeof(IFileManageAppService))]
    internal class FileManageAppService : IFileManageAppService
    {
        private readonly MinioOption _options;
        private readonly IMinioClient _minioClient;

        public FileManageAppService(IOptions<MinioOption> options, IMinioClient minioClient)
        {
            _options = options.Value;
            _minioClient = minioClient;
        }

        public async Task<FileManageOutputDto> GetFile(string bucketName, string fileName)
        {
            if (string.IsNullOrEmpty(bucketName))
            {
                throw new ArgumentNullException(nameof(bucketName) + "必传");
            }
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException(nameof(fileName) + "必传");
            }

            using var ms = new MemoryStream();
            var objArgs = new GetObjectArgs().WithBucket(bucketName)
                .WithObject(fileName)
                .WithCallbackStream(m => m.CopyTo(ms));

            var obj = await _minioClient.GetObjectAsync(objArgs);

            return new FileManageOutputDto()
            {
                Data = ms.ToArray(),
                ContentType = obj.ContentType,
                FileName = fileName,
            };
        }

        public async Task<ResultDto<string>> UploadFile(IFormFile file)
        {
            var bucketArgs = new BucketExistsArgs().WithBucket(_options.DefaultBucket);
            var isExit = await _minioClient.BucketExistsAsync(bucketArgs);

            if (!isExit)
            {
                var mbArgs = new MakeBucketArgs().WithBucket(_options.DefaultBucket);

                await _minioClient.MakeBucketAsync(mbArgs);
            }
            var fileName = file.FileName;
            var fileInfo = await GetFileInfo(_options.DefaultBucket, fileName);
            var index = 1;
            while (fileInfo != null)
            {
                fileName = Path.GetFileNameWithoutExtension(file.FileName) + $"_{index}" + Path.GetExtension(file.FileName);
                fileInfo = await GetFileInfo(_options.DefaultBucket, fileName);
                index++;
            }

            var objArgs = new PutObjectArgs().WithBucket(_options.DefaultBucket)
                .WithObject(fileName)
                .WithObjectSize(file.Length)
                .WithContentType(file.ContentType)
                .WithStreamData(file.OpenReadStream());

            var obj = await _minioClient.PutObjectAsync(objArgs);

            return new ResultDto<string>() { Code = "0", Data = $"{_options.DefaultBucket}/{obj.ObjectName}" };
        }

        public async Task DeleteFile(string bucketName, string fileName)
        {
            if (string.IsNullOrEmpty(bucketName))
            {
                throw new ArgumentNullException(nameof(bucketName) + "必传");
            }
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException(nameof(fileName) + "必传");
            }

            var args = new RemoveObjectArgs()
                .WithBucket(bucketName)
                .WithObject(fileName);

            await _minioClient.RemoveObjectAsync(args);
        }

        public async Task<ObjectStat?> GetFileInfo(string bucketName, string fileName)
        {
            if (string.IsNullOrEmpty(bucketName))
            {
                throw new ArgumentNullException(nameof(bucketName) + "必传");
            }
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException(nameof(fileName) + "必传");
            }

            var args = new StatObjectArgs()
                .WithBucket(bucketName)
                .WithObject(fileName);

            var result = await _minioClient.StatObjectAsync(args);

            return result.Size == 0 ? null : result;
        }

        public Task<byte[]> GetVerificationCode()
        {
            var generator = new CaptchaGenerator();

            var (code, image) = generator.GenerateCaptcha();

            return Task.FromResult(image);
        }
    }
}
